package ecoll.server.quiz;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.Random;

import com.google.appengine.api.datastore.DatastoreService;
import com.google.appengine.api.datastore.DatastoreServiceFactory;
import com.google.appengine.api.datastore.Entity;
import com.google.appengine.api.datastore.EntityNotFoundException;
import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.KeyFactory;
import com.google.appengine.api.datastore.PreparedQuery;
import com.google.appengine.api.datastore.Query;
import com.google.appengine.api.datastore.Query.SortDirection;
import com.google.appengine.api.datastore.Transaction;

import ecoll.client.dictionary.Definition;
import ecoll.client.quiz.QuizUserData;
import ecoll.server.Database;
import ecoll.shared.Constant;

public abstract class QuizDatabase {

	public static QuizRoom getRoom(String roomName) {
		DatastoreService datastore = DatastoreServiceFactory
				.getDatastoreService();

		Key roomKey = KeyFactory.createKey("QuizRoomEntity", roomName);
		Entity roomEntity;
		try {
			roomEntity = datastore.get(roomKey);
		} catch (EntityNotFoundException ex) {
			return null;
		}
		Date date = (Date) roomEntity.getProperty("date");
		int period = ((Long) roomEntity.getProperty("period")).intValue();
		int delay = ((Long) roomEntity.getProperty("delay")).intValue();
		int round = ((Long) roomEntity.getProperty("round")).intValue();
		Key questionKey = (Key) roomEntity.getProperty("question");

		QuizQuestion question = null;
		if (questionKey != null) {
			try {
				Entity questionEntity = datastore.get(questionKey);
				question = new QuizQuestion(questionEntity);
			} catch (EntityNotFoundException ex) {
				return null;
			}
		}
		return new QuizRoom(roomName, date, period, delay, round, question);
	}

	public static Key putRoom(String roomName, int period, int delay,
			ArrayList<Long> files) {

		ArrayList<Definition> definitions = new ArrayList<Definition>();
		for (Long fileId : files) {
			definitions.addAll(Database.getDefinitions(fileId));
		}

		DatastoreService datastore = DatastoreServiceFactory
				.getDatastoreService();
		for (int i = 0; i < Constant.TRIES; ++i) {
			Transaction txn = datastore.beginTransaction();
			try {
				try {
					Key roomKey = KeyFactory.createKey("QuizRoomEntity",
							roomName);
					datastore.get(roomKey);
					txn.commit();
					return null;
				} catch (EntityNotFoundException ex) {
				}
				Entity roomEntity = new Entity("QuizRoomEntity", roomName);
				roomEntity.setProperty("date", new Date());
				roomEntity.setProperty("period", period);
				roomEntity.setProperty("delay", delay);
				roomEntity.setProperty("users", 0);
				roomEntity.setProperty("round", 0);
				roomEntity.setProperty("question", null);
				datastore.put(roomEntity);
				Key roomKey = roomEntity.getKey();

				int cnt = 0;
				for (Definition definition : definitions) {

					String question = "Provide the word: "
							+ definition.getMeaning();
					ArrayList<String> answers = new ArrayList<String>(
							Arrays.asList(definition.getWord()));

					Entity questionEntity = new Entity("QuizQuestionEntity",
							++cnt, roomKey);
					questionEntity.setProperty("question", question);
					questionEntity.setProperty("answers", answers);
					datastore.put(questionEntity);

					if (definition.getSynonyms().get(0).equals(""))
						continue;

					question = "Provide a synonym: " + definition.getWord()
							+ " (" + definition.getMeaning() + ")";
					answers = definition.getSynonyms();

					questionEntity = new Entity("QuizQuestionEntity", ++cnt,
							roomKey);
					questionEntity.setProperty("question", question);
					questionEntity.setProperty("answers", answers);
					datastore.put(questionEntity);
				}

				roomEntity.setProperty("questionCount", cnt);
				datastore.put(roomEntity);

				txn.commit();
				return roomKey;
			} finally {
				if (txn.isActive()) {
					txn.rollback();
				}
			}
		}
		return null;
	}

	public static void deleteRoom(String roomName) {
		DatastoreService datastore = DatastoreServiceFactory
				.getDatastoreService();
		for (int i = 0; i < Constant.TRIES; ++i) {
			Transaction txn = datastore.beginTransaction();
			try {
				Key roomKey = KeyFactory.createKey("QuizRoomEntity", roomName);

				Query roomQuery = new Query().setAncestor(roomKey)
						.setKeysOnly();
				PreparedQuery roomPQ = datastore.prepare(roomQuery);

				for (Entity childEntity : roomPQ.asIterable())
					datastore.delete(childEntity.getKey());

				datastore.delete(roomKey);
				txn.commit();
				return;
			} finally {
				if (txn.isActive()) {
					txn.rollback();
				}
			}
		}
	}

	public static ArrayList<QuizUserData> getUsers(String roomName,
			boolean sorted) {
		Key roomKey = KeyFactory.createKey("QuizRoomEntity", roomName);
		Query query = new Query("QuizUserEntity").setAncestor(roomKey);
		if (sorted)
			query.addSort("score", SortDirection.ASCENDING);

		PreparedQuery pq = DatastoreServiceFactory.getDatastoreService()
				.prepare(query);
		ArrayList<QuizUserData> users = new ArrayList<QuizUserData>();
		for (Entity result : pq.asIterable()) {
			String login = result.getKey().getName();
			String channel = (String) result.getProperty("channel");
			int score = ((Long) result.getProperty("score")).intValue();
			users.add(new QuizUserData(login, channel, score));
		}
		return users;
	}

	public static Key putUser(String roomName, String user, String channel) {
		DatastoreService datastore = DatastoreServiceFactory
				.getDatastoreService();
		for (int i = 0; i < Constant.TRIES; ++i) {
			Transaction txn = datastore.beginTransaction();
			try {
				Key roomKey = KeyFactory.createKey("QuizRoomEntity", roomName);
				try {
					Key userKey = KeyFactory.createKey(roomKey,
							"QuizUserEntity", user);
					datastore.get(userKey);
					txn.commit();
					return null;
				} catch (EntityNotFoundException ex) {
				}
				Entity userEntity = new Entity("QuizUserEntity", user, roomKey);
				userEntity.setProperty("channel", channel);
				userEntity.setProperty("score", 0);
				datastore.put(userEntity);

				Entity roomEntity = datastore.get(roomKey);
				long users = (Long) roomEntity.getProperty("users") + 1;
				roomEntity.setProperty("users", users);
				datastore.put(roomEntity);

				txn.commit();
				return userEntity.getKey();
			} catch (EntityNotFoundException ex) {
				return null;
			} finally {
				if (txn.isActive()) {
					txn.rollback();
				}
			}
		}
		return null;
	}

	public static Boolean deleteUser(String roomName, String user) {
		DatastoreService datastore = DatastoreServiceFactory
				.getDatastoreService();
		for (int i = 0; i < Constant.TRIES; ++i) {
			Transaction txn = datastore.beginTransaction();
			try {
				Key roomKey = KeyFactory.createKey("QuizRoomEntity", roomName);
				Key userKey = KeyFactory.createKey(roomKey, "QuizUserEntity",
						user);
				datastore.delete(userKey);

				Entity roomEntity = datastore.get(roomKey);
				long users = (Long) roomEntity.getProperty("users") - 1;
				roomEntity.setProperty("users", users);
				datastore.put(roomEntity);

				txn.commit();
				if (users == 0)
					return true;
				return false;
			} catch (EntityNotFoundException ex) {
				return null;
			} finally {
				if (txn.isActive()) {
					txn.rollback();
				}
			}
		}
		return null;
	}

	public static QuizQuestion newQuestion(String roomName) {
		DatastoreService datastore = DatastoreServiceFactory
				.getDatastoreService();
		for (int i = 0; i < Constant.TRIES; ++i) {
			Transaction txn = datastore.beginTransaction();
			try {
				Key roomKey = KeyFactory.createKey("QuizRoomEntity", roomName);
				Entity roomEntity = datastore.get(roomKey);
				// if (roomEntity.getProperty("question") != null)
				// return null;
				Long questionCount = (Long) roomEntity
						.getProperty("questionCount");
				if (questionCount == 0)
					return null;

				int questionId = new Random().nextInt(questionCount.intValue());
				Key questionKey = KeyFactory.createKey(roomKey,
						"QuizQuestionEntity", questionId + 1);
				roomEntity.setProperty("question", questionKey);
				int round = ((Long) roomEntity.getProperty("round")).intValue();
				roomEntity.setProperty("round", round + 1);
				datastore.put(roomEntity);

				Entity questionEntity = datastore.get(questionKey);
				QuizQuestion question = new QuizQuestion(questionEntity);

				txn.commit();
				return question;
			} catch (EntityNotFoundException e) {
				return null;
			} finally {
				if (txn.isActive()) {
					txn.rollback();
				}
			}
		}
		return null;
	}

	public static Boolean tryAnswer(String roomName, String user, String answer) {
		DatastoreService datastore = DatastoreServiceFactory
				.getDatastoreService();
		for (int i = 0; i < Constant.TRIES; ++i) {
			Transaction txn = datastore.beginTransaction();
			try {
				Key roomKey = KeyFactory.createKey("QuizRoomEntity", roomName);
				Entity roomEntity = datastore.get(roomKey);
				Key questionKey = (Key) roomEntity.getProperty("question");
				if (questionKey == null)
					return false;
				Entity questionEntity = datastore.get(questionKey);
				QuizQuestion question = new QuizQuestion(questionEntity);
				if (!question.check(answer))
					return false;
				roomEntity.setProperty("question", null);
				datastore.put(roomEntity);

				if (!user.equals(UNANSWERED)) {
					Key quizUserKey = KeyFactory.createKey(roomKey,
							"QuizUserEntity", user);
					Entity quizUserEntity = datastore.get(quizUserKey);
					long score = (Long) quizUserEntity.getProperty("score");
					quizUserEntity.setProperty("score", score + 1);
					datastore.put(quizUserEntity);
				}

				txn.commit();
				return true;
			} catch (EntityNotFoundException e) {
				return null;
			} finally {
				if (txn.isActive()) {
					txn.rollback();
				}
			}
		}
		return null;
	}

	public static final String UNANSWERED = "UNANSWERED";
}
